﻿using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading.Tasks;

namespace xycn.framework.ControllerManagement
{
    public class ControllerManager
    {
        /// <summary>
        /// controller库加载错误信息
        /// </summary>
        public static string errMsg = "";

        /// <summary>
        /// 调用器
        /// </summary>
        public static List<ControllerInvoker> controllerInvokers = 
            new List<ControllerInvoker>();

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public static int CreateControllerInvoker(string controller_path)
        {
            int ret = 0;
            
            //加载controller
            List<ControllerInvoker> controller_invokers;
            if (ControllerLoader.LoadControllers(controller_path, 
                out controller_invokers) < 0)
            {
                errMsg = errMsg + "controller加载错误：" +
                     ControllerLoader.errMsg;
                ret =  -1;
            }

            if(controller_invokers!= null &&
                controller_invokers.Count > 0)
                controllerInvokers.AddRange(controller_invokers);
            
            if (controllerInvokers == null)
            {
                errMsg = errMsg + "controller加载告警：未在任务Controller中找到处理HTTP请求的方法.";
                ret = -1;
            }
            //调用动态生成的调用invoker的静态类
            for (int i = 0; i < controllerInvokers.Count; i++) 
            {
                ControllerInvoker invoker = controllerInvokers[i];

                if (invoker == null)
                    continue;
                
                object new_object = null;
                Type NewType = CreateRouteMappedMethodType("InvokControllerMethod" + i.ToString());
                FieldInfo invoker_field = NewType.GetField("controllerInvoker");
                invoker_field.SetValue(Activator.CreateInstance(NewType), invoker);
                MethodInfo methodInfo = NewType.GetMethod("RouteMappedMethod");

                //当调用方法为非静态方法时，需要输入调用该方法的对象参数
                //Func<HttpContext, Task> m1 = methodInfo.CreateDelegate<Func<HttpContext, Task>>(new_object);
                //当调用方法为静态方法时，无需要输入参数
                Func<HttpContext, Task> dynamic_caller = methodInfo.CreateDelegate<Func<HttpContext, Task>>();

                invoker.dynamicCaller = new RequestDelegate(dynamic_caller);
            }
            return ret;
        }


        /// <summary>
        /// 创建动态路由-方法类，以便将路由映射到对应的调用方法上
        /// </summary>
        /// <param name="type_name"></param>
        /// <param name="create_obj"></param>
        /// <returns></returns>
        public static Type CreateRouteMappedMethodType(string type_name)
        {
            var route_mappedmethod_type = CompileType(type_name);
            return route_mappedmethod_type;
        }

        /// <summary>
        /// 编译路由对应接口调用类
        /// </summary>
        /// <param name="type_name"></param>
        /// <returns></returns>
        private static Type CompileType(string type_name)
        {
            //动态创建程序集
            var typeSignature = type_name;
            var an = new AssemblyName(typeSignature);
            AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(an,
                AssemblyBuilderAccess.Run);
            ModuleBuilder moduleBuilder =
                assemblyBuilder.DefineDynamicModule("xl.DynamicControllerCallerModule");
            TypeBuilder tb = moduleBuilder.DefineType(typeSignature,
                    TypeAttributes.Public |
                    TypeAttributes.Class |
                    TypeAttributes.AutoClass |
                    TypeAttributes.AnsiClass |
                    TypeAttributes.BeforeFieldInit |
                    TypeAttributes.AutoLayout);

            //创建类中字段
            FieldBuilder controllerInvoker = tb.DefineField("controllerInvoker",
                typeof(ControllerInvoker),
                FieldAttributes.Public | FieldAttributes.Static);

            //创建类构造函数
            ConstructorBuilder constructor =
                tb.DefineDefaultConstructor(MethodAttributes.Public |
            MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);

            //使用emit创建调用invoker的函数
            //其函数原型如下：
            //public static Task RouteMappedMethod(HttpContext context)
            //{
            //    if (controllerInvoker == null)
            //    {
            //        return Task.CompletedTask;
            //    }
            //    Console.WriteLine("call");
            //    return controllerInvoker.ControllerDelegate(context);
            //}


            MethodBuilder get_method = tb.DefineMethod("RouteMappedMethod"
                , MethodAttributes.Public | MethodAttributes.Static //| MethodAttributes.Static//MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
                , typeof(Task)
                , new Type[] { typeof(HttpContext) });
            //ConstructorBuilder cb1 = tb.DefineConstructor(MethodAttributes.Public
            //    ,CallingConventions.Standard, null);       

            ILGenerator getIl = get_method.GetILGenerator();
            LocalBuilder condition = getIl.DeclareLocal(typeof(bool));
            LocalBuilder local_task = getIl.DeclareLocal(typeof(Task));

            //定义跳转标志
            Label jump_label_1 = getIl.DefineLabel();
            Label jump_label_2 = getIl.DefineLabel();

            getIl.Emit(OpCodes.Nop);

            //hack
            //打印调试
            /*getIl.Emit(OpCodes.Ldstr, "enter");
            getIl.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", 
                new Type[]{ typeof(string) }));
            getIl.Emit(OpCodes.Nop);*/
            //hack over

            getIl.Emit(OpCodes.Ldsfld, controllerInvoker);
            getIl.Emit(OpCodes.Ldnull);
            getIl.Emit(OpCodes.Ceq);
            getIl.Emit(OpCodes.Stloc_0);
            getIl.Emit(OpCodes.Ldloc_0);
            getIl.Emit(OpCodes.Brfalse_S, jump_label_1); //跳转
            getIl.Emit(OpCodes.Nop);

            //hack
            //打印调试
            /*getIl.Emit(OpCodes.Ldstr, "invoker is null");
            getIl.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine",
                new Type[] { typeof(string) }));
            getIl.Emit(OpCodes.Nop);*/
            //hack over

            getIl.Emit(OpCodes.Call, typeof(Task).GetMethod("get_CompletedTask",
                Type.EmptyTypes));
            getIl.Emit(OpCodes.Stloc_1);


            //hack
            //打印调试
            /*getIl.Emit(OpCodes.Ldstr, "begin jump");
            getIl.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine",
                new Type[] { typeof(string) }));
            getIl.Emit(OpCodes.Nop);*/
            //hack over

            getIl.Emit(OpCodes.Br_S, jump_label_2); //跳转
            getIl.MarkLabel(jump_label_1); //设定跳转标记

            //hack
            //打印调试
            /*getIl.Emit(OpCodes.Ldstr, "call");
            getIl.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine",
                new Type[] { typeof(string) }));
            getIl.Emit(OpCodes.Nop)*/
            //hack over

            getIl.Emit(OpCodes.Ldsfld, controllerInvoker);
            getIl.Emit(OpCodes.Ldarg_0);
            getIl.Emit(OpCodes.Callvirt,
                typeof(ControllerInvoker).GetMethod("ControllerDelegate",
                new Type[] { typeof(HttpContext) }));
            getIl.Emit(OpCodes.Stloc_1);
            getIl.Emit(OpCodes.Br_S, jump_label_2); //跳转
            getIl.MarkLabel(jump_label_2); //设定跳转标记

            //hack
            //打印调试
            /*getIl.Emit(OpCodes.Ldstr, "jumped");
            getIl.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine",
                new Type[] { typeof(string) }));
            getIl.Emit(OpCodes.Nop);*/
            //hack over

            getIl.Emit(OpCodes.Ldloc_1);
            getIl.Emit(OpCodes.Ret);

            //创建类型
            Type objectType = tb.CreateType();
            return objectType;
        }
    }
}

